#asteriskQuest.tcl

package require base64

proc init { } {
  global logWindow state tiles map CONFIG
  set CONFIG(tileSize) 8
  set logWindow ".log"
  toplevel $logWindow
  text ${logWindow}.txtLog -font {Courier 10}
  place ${logWindow}.txtLog -x 0 -y 0 -relwidth 1.0 -relheight 1.0
  wm geometry $logWindow "800x400"

  canvas .cMain -background #FF0000
  place .cMain -x 0 -y 0 -relwidth 1.0 -relheight 1.0


  log "Welcome to asteriskQuest"

  set state new
  set s [connectToServer localhost 51413]
  authenticate $s john abcd

}

proc log { msg } {
  global logWindow
  if {[${logWindow}.txtLog count 0.0 end] > 10000} {
    set newText [string range [${logWindow}.txtLog get 0.0 end] 8000 end]
    ${logWindow}.txtLog delete 0.0 end
    ${logWindow}.txtLog insert 0.0 $newText
  }
  ${logWindow}.txtLog insert end "$msg\n"
  ${logWindow}.txtLog see end
  update; update idletasks
}

proc connectToServer { host port } {
  log "Connecting to ${host}:${port}"
  set s [socket $host $port]
  log "Connected"

  fconfigure $s -blocking 1 -buffering line -translation binary

  gets $s line

  log "Connected to $line"

  fconfigure $s -blocking 1 -buffering line -translation binary

  fileevent $s readable "readSocket $s"
  return $s
}

proc authenticate {s username password} {
  global state
  set state authResponse
  puts $s "LOGIN $username $password"
}

proc readSocket { s } {
  fileevent $s readable ""
  if {[gets $s line] < 0} {
    if {[eof $s]} {
      closeSocket $s
    }
  } else {
    handleCommand $s $line
    fileevent $s readable "readSocket $s"
  }
}

proc closeSocket { s } {
  log "Closing $s"
  close $s
}

proc handleCommand { s line } {
  global state
  switch -exact -- $state {
    new {
      #this must be some kind of hello, welcome message, just log it and move on
      log $line
    }
    authResponse {
      foreach {code text} [split $line :] {break}
      if {[string equal $code "OK"]} {
        log "Login success"
        set state updates
      } else {
        log "$code $text"
        set state new
      }
    }
    updates {
      #log $line
      foreach obj $line {
        log "Got [lindex $obj 0]"
        handleUpdate $obj
      }
    }
    default {
      log "invalid state $state, \"$line\""
    }
  }
}

proc handleUpdate {data} {
  switch -exact -- [lindex $data 0] {
    TILE {updateTile $data}
    FULLMAP {updateFullmap $data}
    PLAYER {updatePlayer $data}
    NPC {updateNPC $data}
    default {}
  }
}

proc updateNPC { data } {
  global CONFIG tiles
  set tileSize $CONFIG(tileSize)
  #npc=NPC tileID npcX npcY npcStats npcType npcName npcDescription
  foreach {NPC tileID x y stats type name description} $data {break}
  .cMain delete $name
  if {[info exists tiles($tileID)]} {
    .cMain create image [expr {round($x * $tileSize) + 2}] [expr {round($y * $tileSize) + 2}] -anchor center -image $tiles($tileID) -tags $name
  } else {
    log "Error, NPC $name tile is $tileID but that tile is not found"
  }
}

proc updatePlayer { data } {
  global CONFIG tiles
  #player=PLAYER tileID playerX playerY playerName playerDirection playerStats itemList
  foreach {PLAYER tileID x y name direction stats items} $data {break}
  set tileSize $CONFIG(tileSize)
  .cMain delete $name
  if {[info exists tiles($tileID)]} {
    .cMain create image [expr {round($x * $tileSize) + 2}] [expr {round($y * $tileSize) + 2}] -anchor center -image $tiles($tileID) -tags $name
  } else {
    log "Error, player tile is $tileID but that tile is not found"
  }
}

proc updateTile { data } {
  global tiles CONFIG
  #tile=TILE tileID bitmap
  foreach {TILE ID bitmap} $data {break}
  if {[info exists tiles($ID)]} {
    set tileImage $tiles($ID)
    $tileImage put [::base64::decode $bitmap]
  } else {
    set tileImage [image create photo -width $CONFIG(tileSize) -height $CONFIG(tileSize)]
    $tileImage put [::base64::decode $bitmap]
    set tiles($ID) $tileImage
  }
}

proc updateFullmap { data } {
  global map CONFIG tiles
  foreach {FULLMAP name tileData obstructionData} $data {break}
  set map $tileData
  set mapHeight [llength $tileData]
  set mapWidth [llength [lindex $tileData 0]]
  set tileSize $CONFIG(tileSize)
  for {set y 0} {$y < $mapHeight} {incr y} {
    for {set x 0} {$x < $mapWidth} {incr x} {
      set tileID [lindex $tileData $y $x]
      if {[info exists tiles($tileID)]} {
        #must offset by two, canvas origin is wrong...don't know why
        .cMain create image [expr {$x * $tileSize + 2}] [expr {$y * $tileSize + 2}] -anchor nw -image $tiles($tileID)
      } else {
        .cMain create rect [expr {$x * $tileSize+2}] [expr {$y * $tileSize+2}] [expr {($x + 1) * $tileSize - 1+2}] [expr {($y + 1) * $tileSize - 1+2}] -width 1 -outline #000000 -fill #FF0000
      }
    }
  }
}

init
